package be.kuleuven.thesis.neo4j;
//import java.util.concurrent.TimeUnit;

import java.util.Iterator;

import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.graphdb.schema.Schema;
//import org.neo4j.kernel.api.exceptions.schema.AlreadyIndexedException;

import be.kuleuven.thesis.model.Gene;
import be.kuleuven.thesis.model.NonGene;
import be.kuleuven.thesis.model.Protein;


public class Neo4jDBManager {
	private static final String DB_PATH = "/home/moni/Documents/BioinformaticsMaster/Thesis/SNPdatabase/neo4j-community-2.0.0-M05/data/neo4j-db";
	GraphDatabaseService graphDb;
	
	
	public Neo4jDBManager(){
		graphDb = (GraphDatabaseService) new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
		registerShutdownHook( graphDb );
	}
	
	private static void registerShutdownHook( final GraphDatabaseService graphDb )
	{
	    // Registers a shutdown hook for the Neo4j instance so that it
	    // shuts down nicely when the VM exits (even if you "Ctrl-C" the
	    // running application).
	    Runtime.getRuntime().addShutdownHook( new Thread()
	    {
	        @Override
	        public void run()
	        {
	            graphDb.shutdown();
	        }
	    } );
	}
	
	public GraphDatabaseService getGraphDb() {
		return graphDb;
	}
	
	
	public static enum RelTypes implements RelationshipType{
		  ENCODES, SNP_SAMPLE, SUBMISSION
	  }
		 
	
	public void createIndexLabelProperty(Label label, String property){
	
		try ( Transaction tx = graphDb.beginTx() )
		{
			Schema schema = graphDb.schema();
			//Iterator<IndexDefinition> indexes = schema.getIndexes(label).iterator();
			Iterable<IndexDefinition> indexes = schema.getIndexes(label);
			//ArrayList<IndexDefinition> labelIndexes = new ArrayList<>();
			
			if(indexes.iterator().hasNext()){
				for(IndexDefinition index : indexes){
					Iterator<String> prop = index.getPropertyKeys().iterator();
					while(prop.hasNext()){
						if(prop.next().equals(property)){
							System.out.println("Label: "+label.name()+ " already indexed for given property: " + property);
							tx. success();
							return;
						}
						
					}			
				}
				schema.indexFor(label)
	            .on( property )
	            .create();
			}else {
				schema.indexFor(label)
			            .on( property )
			            .create();
			} 	
		    tx.success();
		}
	}
	
	public Node getOrCreateNode(Label label, String indexProperty, String indexValue){
		Node node;
	
		try ( Transaction tx = graphDb.beginTx()) {
			ResourceIterator<Node> batchNodes = graphDb.findNodesByLabelAndProperty(label, 
					indexProperty, indexValue ).iterator();
			if(batchNodes.hasNext()){
				System.out.print("The " + label.name() +" node " + indexValue + " already exists, returning already created node");
				node = batchNodes.next();
			}else{
				node = graphDb.createNode(label);
				node.setProperty(indexProperty, indexValue);
			}
			tx.success();
		}
		
		return node;
	}
	
	public Node createNonGeneNode(NonGene nonGene){
		String propertyIndex = "locus_tag";
		Label nonGeneLabel = DynamicLabel.label("nongene");
		createIndexLabelProperty(nonGeneLabel, propertyIndex);
		
		Node nonGeneNode = graphDb.createNode(nonGeneLabel);
		nonGeneNode.setProperty(propertyIndex, nonGene.getLocusTag());
		nonGeneNode.setProperty( "location", nonGene.getRawLocation());
	    nonGeneNode.setProperty("seqType", nonGene.getSeqType());
	    nonGeneNode.setProperty("seqId", nonGene.getSeqId());
        nonGeneNode.setProperty("strand", nonGene.getStrand());
        
        if(nonGene.hasNcRNAClass())
        	nonGeneNode.setProperty("ncRNA_class", nonGene.getNcRNAClass());
        
        if(nonGene.hasProduct())
        	nonGeneNode.setProperty("product", nonGene.getProduct());
        
		return nonGeneNode;
	}
	
	public Node createGeneNode(Gene gene){
		String propertyIndex = "locus_tag";
		Label geneLabel = DynamicLabel.label("gene");
		createIndexLabelProperty(geneLabel, propertyIndex);
		Node geneNode = graphDb.createNode(geneLabel);
		
		geneNode.setProperty(propertyIndex, gene.getLocusTag());
		geneNode.setProperty( "location", gene.getRawLocation());
	    geneNode.setProperty("seqType", gene.getSeqType());
	    geneNode.setProperty("seqId", gene.getSeqId());
        geneNode.setProperty("strand", gene.getStrand());
		geneNode.setProperty("protein", gene.getProtein());
		geneNode.setProperty("dbRef", gene.getDbRef());
		
		return geneNode;
	}
	
	public Node createProteinNode(Protein protein){
		String propertyIndex = "protein_id";
		Label proteinLabel = DynamicLabel.label("protein");
		createIndexLabelProperty(proteinLabel, propertyIndex);
		Node proteinNode = graphDb.createNode(proteinLabel);
		proteinNode.setProperty(propertyIndex, protein.getProteinId());
		
		if(protein.getProduct() != null)
			proteinNode.setProperty("product", protein.getProduct());
		
		if(protein.getDbRef() != null)
			proteinNode.setProperty("db_xref", protein.getDbRef());
		
		return proteinNode;
	}
	
	
	
}
